/*
  Distributed under the terms of GNU LGPL
  (c) 2009 Paul Graphov <graphov@gmail.com>
*/

#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <setjmp.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <locale.h>

#include "list.h"
#include "zepto.h"
#include "config.h"
#include "internal.h"

#define __DEFINE_TABLE__

// When error occurs this buffer is used for long
// jump to error handling. This allows to avoid
// hundreds of return code checks and seem to be
// enough flexible for typical cases.
static jmp_buf err_buf;

// as all booleans are immutable, we can use
// the same values for all truthes and lies:
struct zepto __holy_truth = {
  .type = ZEPTO_BOOL,
  .data.boolean = true,
};

struct zepto __unholy_lie = {
  .type = ZEPTO_BOOL,
  .data.boolean = false,
};

// NILs also...
struct zepto __nil = {
  .type = ZEPTO_NIL,
};

// zero and one also...
struct zepto __zero = {
  .type = ZEPTO_INTEGER,
  .data.integer = 0,
};

struct zepto __one = {
  .type = ZEPTO_INTEGER,
  .data.integer = 1,
};

struct zepto *__quote;

/**
 * Set error handler long jump buffer.
 *
 * @return one when handler is invoked. 
 */
int zepto_error_handler()
{
  if(setjmp(err_buf)) {
    //TODO: add some gc-related cleanup;
    return 1;
  } else {
    return 0;
  }
}

// FIXME: make it all report errors instead of long jumping
// (at least toplevel env queries)
/*
static struct zepto *_get_toplevel(const char *name)
{
  // TODO:
  return NULL;
}
*/

/**
 * Invoke error interpreter's handler.
 */
void zepto_error(void)
{
  longjmp(err_buf, 1);
}

/**
 * Get an integer value bound in toplevel environment.
 * @param name symbol name.
 * @return integer value.
 */
long zepto_get_toplevel_int(const char *name)
{
  struct zepto *res_expr;
  struct zepto *sym;

  sym = sym_lookup(name);

  res_expr = env_lookup(&toplevel_env, sym);
  CHECK_TYPE_SAFE(res_expr, ZEPTO_INTEGER);

  return res_expr->data.integer;
}

/**
 * Get string value bound in toplevel environment.
 * @param name symbol name.
 * @return string value.
 */
char *zepto_get_toplevel_string(const char *name)
{
  struct zepto *res_expr;
  struct zepto *sym;
  char *str;
  int len;

  //TODO:
  sym = sym_lookup(name);
  
  res_expr = env_lookup(&toplevel_env, sym);
  CHECK_TYPE_SAFE(res_expr, ZEPTO_STRING);

  len = wcstombs(NULL, res_expr->data.string.data, 0);
  
  ASSERT(len > 0);
    
  str = zepto_alloc_general(len);
  
  wcstombs(str, res_expr->data.string.data, len);

  return str;
}

/**
 * Load and evaluate file in toplevel.
 * @param path path to file
 */
void zepto_load_file(const char *path)
{
  stream_t *file_str;
  struct zepto *expr;

  file_str = _stream_open_file(path, STREAM_IN);

  if(!file_str) {
    ERROR("Cannot open file.");
  }

  while((expr = _stream_read_expr(file_str)) != NULL) {
    eval(&toplevel_env, expr);
  }
}

/**
 * Evaluate string in toplevel.
 * @param string string to evaluate
 */
void zepto_eval_string(const char *string)
{

}

/**
 * Initialize internal structures.
 */
void zepto_init(void)
{
  sym_init(0);
  
  env_init_toplevel(DEFAULT_TOPLEVEL_SIZE);

  setlocale(LC_ALL, "");
  
  add_builtins();

  if(setjmp(err_buf)) {
    fprintf(stderr, "Zeptoscheme interpreter error.\n");
    abort();
  }
}
